<!DOCTYPE html>
<html lang="zh">

    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>WebSocket Chat</title>
        <style>
            :root {
                --system-color: #f0f0f0;
                --public-color: #e3f2fd;
                --private-color: #ffebee;
                --error-color: #ffcdd2;
                --binary-color: #e8f5e9;
                --error-text: #c62828;
                --border-color: #ccc;
                --shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
            }

            body {
                font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
                max-width: 1000px;
                margin: 0 auto;
                padding: 20px;
                background-color: #f9f9f9;
            }

            .chat-container {
                display: grid;
                grid-template-columns: 1fr 250px;
                gap: 20px;
            }

            #chat {
                height: 500px;
                border: 1px solid var(--border-color);
                border-radius: 8px;
                overflow-y: auto;
                padding: 15px;
                background-color: white;
                box-shadow: var(--shadow);
            }

            .message {
                margin-bottom: 12px;
                padding: 10px;
                border-radius: 8px;
                word-break: break-word;
                box-shadow: var(--shadow);
            }

            .system {
                background-color: var(--system-color);
            }

            .public {
                background-color: var(--public-color);
            }

            .private {
                background-color: var(--private-color);
            }

            .error {
                background-color: var(--error-color);
                color: var(--error-text);
            }

            .binary {
                background-color: var(--binary-color);
            }

            .message-time {
                font-size: 0.8em;
                color: #666;
                margin-left: 10px;
            }

            #userList {
                border: 1px solid var(--border-color);
                border-radius: 8px;
                padding: 15px;
                background-color: white;
                box-shadow: var(--shadow);
                height: fit-content;
            }

            #users {
                list-style: none;
                padding: 0;
            }

            #users li {
                padding: 8px;
                margin-bottom: 5px;
                background-color: #f5f5f5;
                border-radius: 4px;
            }

            .form-group {
                margin-bottom: 15px;
            }

            input,
            button,
            select {
                padding: 10px;
                border: 1px solid var(--border-color);
                border-radius: 4px;
                font-size: 16px;
            }

            button {
                background-color: #4CAF50;
                color: white;
                cursor: pointer;
                transition: background-color 0.3s;
            }

            button:hover {
                background-color: #45a049;
            }

            button:disabled {
                background-color: #cccccc;
                cursor: not-allowed;
            }

            .tab-container {
                display: flex;
                margin-bottom: 15px;
            }

            .tab {
                padding: 10px 20px;
                cursor: pointer;
                border: 1px solid var(--border-color);
                background-color: #f1f1f1;
            }

            .tab.active {
                background-color: #ddd;
            }

            .tab-content {
                display: none;
            }

            .tab-content.active {
                display: block;
            }

            #fileInfo {
                margin-top: 10px;
                font-size: 0.9em;
                color: #666;
            }

            .connection-status {
                display: inline-block;
                width: 12px;
                height: 12px;
                border-radius: 50%;
                margin-left: 10px;
            }

            .status-connected {
                background-color: #4CAF50;
            }

            .status-disconnected {
                background-color: #f44336;
            }

            .status-connecting {
                background-color: #FFC107;
            }
        </style>
    </head>

    <body>
        <h1>WebSocket Chat <span id="connectionStatus" class="connection-status status-disconnected"></span></h1>

        <div class="chat-container">
            <div>
                <div id="chat"></div>

                <div class="tab-container">
                    <div class="tab active" data-tab="login">登录</div>
                    <div class="tab" data-tab="public">群聊</div>
                    <div class="tab" data-tab="private">私聊</div>
                    <div class="tab" data-tab="file">文件</div>
                </div>

                <div id="login" class="tab-content active">
                    <div class="form-group">
                        <input type="text" id="username" placeholder="用户名" required>
                    </div>
                    <div class="form-group">
                        <input type="password" id="password" placeholder="密码" required>
                    </div>
                    <button id="loginBtn">登录</button>
                </div>

                <div id="public" class="tab-content">
                    <div class="form-group">
                        <input type="text" id="messageText" placeholder="输入消息" autocomplete="off" disabled>
                    </div>
                    <button id="sendBtn" disabled>发送</button>
                </div>

                <div id="private" class="tab-content">
                    <div class="form-group">
                        <select id="privateTo" disabled>
                            <option value="">选择接收人</option>
                        </select>
                    </div>
                    <div class="form-group">
                        <input type="text" id="privateMessage" placeholder="私聊消息" disabled>
                    </div>
                    <button id="sendPrivateBtn" disabled>发送</button>
                </div>

                <div id="file" class="tab-content">
                    <div class="form-group">
                        <input type="file" id="fileInput" disabled>
                    </div>
                    <div id="fileInfo"></div>
                    <button id="sendFileBtn" disabled>发送文件</button>
                </div>
            </div>

            <div id="userList">
                <h3>在线用户 (<span id="userCount">0</span>)</h3>
                <ul id="users"></ul>
            </div>
        </div>

        <script>
            class ChatApp {
                constructor() {
                    this.ws = null;
                    this.currentUser = null;
                    this.currentToken = null;
                    this.reconnectAttempts = 0;
                    this.maxReconnectAttempts = 5;
                    this.reconnectInterval = 5000; // 5秒

                    this.initElements();
                    this.initEventListeners();
                    this.initTabs();
                }

                initElements() {
                    this.chatDiv = document.getElementById("chat");
                    this.usersList = document.getElementById("users");
                    this.userCountSpan = document.getElementById("userCount");
                    this.connectionStatus = document.getElementById("connectionStatus");

                    // 表单元素
                    this.usernameInput = document.getElementById("username");
                    this.passwordInput = document.getElementById("password");
                    this.loginBtn = document.getElementById("loginBtn");

                    this.messageTextInput = document.getElementById("messageText");
                    this.sendBtn = document.getElementById("sendBtn");

                    this.privateToSelect = document.getElementById("privateTo");
                    this.privateMessageInput = document.getElementById("privateMessage");
                    this.sendPrivateBtn = document.getElementById("sendPrivateBtn");

                    this.fileInput = document.getElementById("fileInput");
                    this.fileInfoDiv = document.getElementById("fileInfo");
                    this.sendFileBtn = document.getElementById("sendFileBtn");
                }

                initEventListeners() {
                    this.loginBtn.addEventListener('click', () => this.handleLogin());

                    this.sendBtn.addEventListener('click', (e) => {
                        e.preventDefault();
                        this.sendPublicMessage();
                    });

                    this.sendPrivateBtn.addEventListener('click', (e) => {
                        e.preventDefault();
                        this.sendPrivateMessage();
                    });

                    this.sendFileBtn.addEventListener('click', (e) => {
                        e.preventDefault();
                        this.sendFile();
                    });

                    this.messageTextInput.addEventListener('keypress', (e) => {
                        if (e.key === 'Enter') {
                            e.preventDefault();
                            this.sendPublicMessage();
                        }
                    });

                    this.privateMessageInput.addEventListener('keypress', (e) => {
                        if (e.key === 'Enter') {
                            e.preventDefault();
                            this.sendPrivateMessage();
                        }
                    });

                    this.fileInput.addEventListener('change', () => this.updateFileInfo());
                }

                initTabs() {
                    const tabs = document.querySelectorAll('.tab');
                    tabs.forEach(tab => {
                        tab.addEventListener('click', () => {
                            // 移除所有active类
                            document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
                            document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));

                            // 添加active类到当前tab
                            tab.classList.add('active');
                            const tabId = tab.getAttribute('data-tab');
                            document.getElementById(tabId).classList.add('active');
                        });
                    });
                }

                async handleLogin() {
                    const username = this.usernameInput.value.trim();
                    const password = this.passwordInput.value.trim();

                    if (!username || !password) {
                        this.displaySystemMessage("请输入用户名和密码");
                        return;
                    }

                    try {
                        const formData = new FormData();
                        formData.append('account', username);
                        formData.append('password', password);

                        // 调用登录API获取token
                        const response = await fetch('http://127.0.0.1:8000/api/v1/auth/login', {
                            method: 'POST',
                            body: formData
                        });

                        const data = await response.json();

                        if (response.ok) {
                            this.currentToken = data.data.access_token;
                            this.currentUser = username;
                            this.connectWebSocket();

                            // 启用表单
                            this.enableControls();
                            this.displaySystemMessage("登录成功");
                        } else {
                            this.displaySystemMessage(`登录失败: ${data.detail}`);
                        }
                    } catch (error) {
                        this.displaySystemMessage(`登录错误: ${error.message}`);
                    }
                }

                enableControls() {
                    this.messageTextInput.disabled = false;
                    this.sendBtn.disabled = false;
                    this.privateToSelect.disabled = false;
                    this.privateMessageInput.disabled = false;
                    this.fileInput.disabled = false;
                    this.sendFileBtn.disabled = false;
                    this.sendPrivateBtn.disabled = false;
                }

                disableControls() {
                    this.messageTextInput.disabled = true;
                    this.sendBtn.disabled = true;
                    this.privateToSelect.disabled = true;
                    this.privateMessageInput.disabled = true;
                    this.fileInput.disabled = true;
                    this.sendFileBtn.disabled = true;
                    this.sendPrivateBtn.disabled = true;
                }

                connectWebSocket() {
                    if (this.ws) {
                        this.ws.close();
                    }

                    this.updateConnectionStatus('connecting');

                    this.ws = new WebSocket(`ws://127.0.0.1:8000/api/v1/websocket/ws/${encodeURIComponent(this.currentToken)}`);

                    this.ws.onopen = () => {
                        this.updateConnectionStatus('connected');
                        this.reconnectAttempts = 0; // 重置重连尝试次数
                        this.displaySystemMessage(`${this.currentUser} 已连接到聊天服务器`);
                    };

                    this.ws.onmessage = (event) => {
                        try {
                            const data = JSON.parse(event.data);
                            this.handleServerMessage(data);
                        } catch {
                            this.displayTextMessage(event.data);
                        }
                    };

                    this.ws.onclose = (event) => {
                        this.updateConnectionStatus('disconnected');

                        let message = "连接已断开";
                        if (event.code === 1008) {
                            message = "认证失败，请重新登录";
                        }

                        this.displaySystemMessage(message);
                        this.disableControls();

                        // 非正常关闭且未超过最大重连次数时尝试重连
                        if (event.code !== 1000 && this.reconnectAttempts < this.maxReconnectAttempts) {
                            this.reconnectAttempts++;
                            this.displaySystemMessage(`将在 ${this.reconnectInterval / 1000} 秒后尝试重新连接 (${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
                            setTimeout(() => this.connectWebSocket(), this.reconnectInterval);
                        }
                    };

                    this.ws.onerror = (error) => {
                        this.displaySystemMessage(`连接错误: ${error.message}`);
                    };
                }

                updateConnectionStatus(status) {
                    this.connectionStatus.className = `connection-status status-${status}`;
                }

                handleServerMessage(data) {
                    console.log(data);

                    switch (data.type) {
                        case "system":
                            this.displaySystemMessage(data.content);
                            if (data.users) {
                                this.updateUserList(data.users);
                            }
                            break;
                        case "public":
                            this.displayPublicMessage(data.sender, data.content, data.timestamp);
                            break;
                        case "private":
                            this.displayPrivateMessage(data.sender, data.content, data.timestamp);
                            break;
                        case "binary_data":
                            this.displayBinaryMessage(data.sender, data.size);
                            break;
                        case "error":
                            this.displayErrorMessage(data.content);
                            break;
                        default:
                            this.displayTextMessage(JSON.stringify(data));
                    }
                }

                displaySystemMessage(text) {
                    this.appendMessage(text, 'system');
                }

                displayPublicMessage(sender, content, timestamp) {
                    const timeStr = timestamp ? this.formatTime(timestamp) : '';
                    this.appendMessage(`${sender}: ${content}`, 'public', timeStr);
                }

                displayPrivateMessage(sender, content, timestamp) {
                    const timeStr = timestamp ? this.formatTime(timestamp) : '';
                    this.appendMessage(`[私聊 from ${sender}]: ${content}`, 'private', timeStr);
                }

                displayBinaryMessage(sender, size) {
                    this.appendMessage(`${sender} 发送了二进制数据 (${size} bytes)`, 'binary');
                }

                displayErrorMessage(text) {
                    this.appendMessage(`错误: ${text}`, 'error');
                }

                displayTextMessage(text) {
                    this.appendMessage(text, 'public');
                }

                appendMessage(text, className, timeStr = '') {
                    const messageDiv = document.createElement("div");
                    messageDiv.className = `message ${className}`;

                    const messageContent = document.createElement("span");
                    messageContent.textContent = text;
                    messageDiv.appendChild(messageContent);

                    if (timeStr) {
                        const timeSpan = document.createElement("span");
                        timeSpan.className = "message-time";
                        timeSpan.textContent = timeStr;
                        messageDiv.appendChild(timeSpan);
                    }

                    this.chatDiv.appendChild(messageDiv);
                    this.chatDiv.scrollTop = this.chatDiv.scrollHeight;
                }

                formatTime(timestamp) {
                    try {
                        const date = new Date(timestamp);
                        return date.toLocaleTimeString();
                    } catch {
                        return '';
                    }
                }

                updateUserList(users) {
                    this.usersList.innerHTML = '';
                    this.privateToSelect.innerHTML = '<option value="">选择接收人</option>';

                    users.forEach(user => {
                        // 更新用户列表
                        const li = document.createElement("li");
                        li.textContent = user;
                        this.usersList.appendChild(li);

                        // 更新私聊下拉框
                        if (user !== this.currentUser) {
                            const option = document.createElement("option");
                            option.value = user;
                            option.textContent = user;
                            this.privateToSelect.appendChild(option);
                        }
                    });

                    this.userCountSpan.textContent = users.length;
                }

                sendPublicMessage() {
                    const message = this.messageTextInput.value.trim();

                    if (!this.checkConnection() || !message) {
                        return;
                    }

                    this.ws.send(JSON.stringify({
                        type: "public_message",
                        content: message
                    }));

                    this.messageTextInput.value = '';
                }

                sendPrivateMessage() {
                    const recipient = this.privateToSelect.value;
                    const message = this.privateMessageInput.value.trim();

                    if (!this.checkConnection() || !recipient || !message) {
                        return;
                    }

                    this.ws.send(JSON.stringify({
                        type: "private_message",
                        to: recipient,
                        content: message
                    }));

                    this.privateMessageInput.value = '';
                }

                updateFileInfo() {
                    const file = this.fileInput.files[0];
                    if (!file) {
                        this.fileInfoDiv.textContent = '';
                        return;
                    }

                    this.fileInfoDiv.textContent = `文件名: ${file.name} (${this.formatFileSize(file.size)})`;
                }

                formatFileSize(bytes) {
                    if (bytes === 0) return '0 Bytes';
                    const k = 1024;
                    const sizes = ['Bytes', 'KB', 'MB', 'GB'];
                    const i = Math.floor(Math.log(bytes) / Math.log(k));
                    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
                }

                sendFile() {
                    const file = this.fileInput.files[0];

                    if (!this.checkConnection() || !file) {
                        return;
                    }

                    const reader = new FileReader();
                    reader.onload = (e) => {
                        // 先发送元数据
                        this.ws.send(JSON.stringify({
                            type: "binary_metadata",
                            filename: file.name,
                            size: file.size,
                            mimeType: file.type
                        }));

                        // 然后发送二进制数据
                        this.ws.send(e.target.result);

                        this.displaySystemMessage(`正在发送文件: ${file.name}`);
                        this.fileInput.value = '';
                        this.fileInfoDiv.textContent = '';
                    };
                    reader.readAsArrayBuffer(file);
                }

                checkConnection() {
                    if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
                        this.displaySystemMessage("未连接到服务器");
                        return false;
                    }
                    return true;
                }
            }

            // 初始化应用
            document.addEventListener('DOMContentLoaded', () => {
                const app = new ChatApp();
            });
        </script>
    </body>

</html>